using System;
using System.Collections.Generic;
using System.Text;
using AntEater.ComponentModel.BuildElements;
using System.Xml.XPath;
using System.Xml;
using AntEater.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;

namespace AntEater.Runtime.Services.Transformation
{

    /// <summary>
    /// Provides a transfomer for the ImportBuildElement.
    /// </summary>
    [BuildElementTransformer(typeof(ImportBuildElement), XPathNodeType.Element)]
    internal class ImportTransformer : BuildElementTransformerBase
    {

        /// <summary>
        /// Initializes a new instance of the <see cref="T:ImportTransformer"/> class.
        /// </summary>
        /// <param name="context">The context.</param>
        [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        public ImportTransformer(object context)
            : base(context) {
        }

        /// <summary>
        /// Transforms an <c>BuildElement</c> into XML.
        /// </summary>
        /// <param name="buildElement">The build element.</param>
        /// <param name="writer">The writer.</param>
        /// <param name="errors">The errors.</param>
        public override void GetXml(IBuildElement buildElement, XmlWriter writer, List<TransformationError> errors) {
            ImportBuildElement element = buildElement as ImportBuildElement;

            if (element != null) {

                BuildElementAttribute buildElementAttribute
                    = ComponentModel.Utilities.GetTypeAttributeFromObject<BuildElementAttribute>(buildElement);

                if (buildElementAttribute != null) {
                    writer.WriteStartElement(buildElementAttribute.Name);

                    if (!string.IsNullOrEmpty(element.Project)) {
                        writer.WriteAttributeString(this.GetBuildElementPropertyName("Project"), element.Project);
                    }
                    else {
                        errors.Add(new TransformationError("Project attribute isn't specified!", element));
                    }

                    if (!string.IsNullOrEmpty(element.Condition)) {
                        writer.WriteAttributeString(this.GetBuildElementPropertyName("Condition"), element.Condition);
                    }

                    this.OnGetChildElementsXml(element, writer);

                    writer.WriteEndElement();
                }
            }
        }

        /// <summary>
        /// Gets an <c>BuildElement</c> from XML.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="buildFileDefinition"></param>
        /// <param name="errors">The errors.</param>
        /// <returns></returns>
        public override IBuildElement GetBuildElement(XPathNavigator node, BuildFileDefinition buildFileDefinition, List<TransformationError> errors) {
            ImportBuildElement element = buildFileDefinition.CreateNewBuildElement(typeof(ImportBuildElement)) as ImportBuildElement;

            bool projectFound = false;

            if (node.MoveToFirstAttribute()) {
                do {
                    if (node.Name.Equals(this.GetBuildElementPropertyName("Project"))) {
                        element.Project = node.Value;
                        projectFound = true;

                        bool projectTransformed;

                        projectTransformed = this.TransformImportedProject(node, element.Project, element.ImportedRootBuildElement, errors, element, buildFileDefinition);

                        element.Loaded = projectTransformed;

                        if (!projectTransformed) {
                            errors.Add(new TransformationError(string.Format(CultureInfo.CurrentCulture,"Could not load imported Build File. {0}", element.Project), MessageType.Warning, element));
                        }
                    }
                    else if (node.Name.Equals(this.GetBuildElementPropertyName("Condition"))) {
                        element.Condition = node.Value;
                    }
                } while (node.MoveToNextAttribute());
            }

            if (!projectFound) {
                errors.Add(new TransformationError("Attribute Project is missing!", element));
            }

            return element;
        }

        /// <summary>
        /// Transforms the imported project.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="projectPath">The project path.</param>
        /// <param name="rootBuildElement">The root build element.</param>
        /// <param name="errors">The errors.</param>
        /// <param name="parentElement">The parent element.</param>
        /// <param name="buildFileDefinition">The build file definition.</param>
        /// <returns></returns>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")]
        private bool TransformImportedProject(XPathNavigator node, string projectPath, IRootBuildElement rootBuildElement, List<TransformationError> errors, IBuildElement parentElement, BuildFileDefinition buildFileDefinition) {
            bool result = false;

            TransformerContext context = this.Context as TransformerContext;
            XmlToBuildElementTransformationService service = context.Service as XmlToBuildElementTransformationService;

            if (context != null && service != null) {

                XPathNavigator buildFile = node.Clone();
                buildFile.MoveToRoot();

                Microsoft.Build.BuildEngine.Project buildProject = null;

                try {
                    buildProject = Utilities.GetMSBuildProject(buildFile.OuterXml, context.BuildFilePath, service.BuildEnginePath);
                }
                catch (Exception ex) {
                    errors.Add(new TransformationError(string.Format(CultureInfo.CurrentCulture, "Could not load Build File with MSBuild Engine. BuildFilePath='{0}' BuildEnginePath='{1}' Exception='{2}' ", context.BuildFilePath, service.BuildEnginePath, ex.Message), parentElement));
                }

                if (buildProject != null) {
                    string importedProjectPath = null;

                    foreach (Microsoft.Build.BuildEngine.Import import in buildProject.Imports) {
                        if (import.ProjectPath == projectPath) {
                            importedProjectPath = import.EvaluatedProjectPath;
                            break;
                        }
                    }

                    if (!string.IsNullOrEmpty(importedProjectPath)) {
                        XmlDocument importedBuildFile = new XmlDocument();
                        importedBuildFile.Load(importedProjectPath);

                        TransformerContext newTransformerContext = new TransformerContext(service, importedProjectPath);

                        List<IBuildElement> buildElements = service.TransformBuildFile(importedBuildFile.CreateNavigator(), errors, parentElement, buildFileDefinition, newTransformerContext);

                        rootBuildElement.BuildFilePath = importedProjectPath;
                        rootBuildElement.ChildElements.AddRange(buildElements);
                        result = true;
                    }
                }
            }
            return result;
        }
    }
}
